<template>
  <div class="simple-table-wrapper">
    <h3 v-if="title" class="table-title">{{ title }}</h3>

    <div class="table-outer">
      <div class="table-header-fixed">
        <table class="simple-table" :class="{ bordered }">
          <thead v-if="showHeader">
            <tr>
              <template
                v-for="(column, colIndex) in columns"
                :key="column.prop"
              >
                <th
                  v-if="!isHeaderCellMerged(colIndex)"
                  :style="{
                    width: column.width || 'auto',
                    textAlign: column.align || 'left',
                  }"
                  :colspan="column.headerColspan || 1"
                  :rowspan="column.headerRowspan || 1"
                  class="table-header-cell"
                >
                  {{ column.label }}
                </th>
              </template>
            </tr>
          </thead>
        </table>
      </div>

      <div class="table-body-scroll" :style="{ maxHeight }">
        <table class="simple-table" :class="{ striped, bordered }">
          <tbody>
            <template v-for="(row, rowIndex) in data" :key="rowIndex">
              <tr class="table-row" :class="{ 'hover-row': hoverEffect }">
                <template
                  v-for="(column, colIndex) in columns"
                  :key="column.prop"
                >
                  <td
                    v-if="!isBodyCellMerged(rowIndex, colIndex)"
                    class="table-cell"
                    :style="{
                      width: column.width || 'auto',
                      textAlign: column.align || 'left',
                    }"
                    :colspan="getColspan(row, rowIndex, column, colIndex)"
                    :rowspan="getRowspan(row, rowIndex, column, colIndex)"
                  >
                    <slot
                      :name="column.prop"
                      :row="row"
                      :rowIndex="rowIndex"
                      :column="column"
                    >
                      {{ getCellValue(row, column.prop) }}
                    </slot>
                  </td>
                </template>
              </tr>
            </template>

            <tr v-if="data.length === 0">
              <td :colspan="columns.length" class="empty-cell">
                <div class="empty-state">
                  <span class="empty-text">{{ emptyText || "暂无数据" }}</span>
                </div>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import type { PropType } from "vue";
import { ref, onMounted } from "vue";

defineOptions({
  name: "BySimpleTable",
});

interface TableColumn {
  prop: string;
  label: string;
  width?: string;
  align?: "left" | "center" | "right";
  headerColspan?: number;
  headerRowspan?: number;
  mergeCell?: (
    row: Record<string, any>,
    rowIndex: number,
    column: TableColumn,
    colIndex: number,
    data: Record<string, any>[]
  ) => { colspan: number; rowspan: number };
  mergeByField?: boolean;
}

const props = defineProps({
  title: {
    type: String,
    default: "",
  },
  data: {
    type: Array as PropType<Record<string, any>[]>,
    default: () => [],
  },
  columns: {
    type: Array as PropType<TableColumn[]>,
    default: () => [],
  },
  showHeader: {
    type: Boolean,
    default: true,
  },
  striped: {
    type: Boolean,
    default: false,
  },
  bordered: {
    type: Boolean,
    default: false,
  },
  hoverEffect: {
    type: Boolean,
    default: true,
  },
  maxHeight: {
    type: String,
    default: "400px",
  },
  emptyText: {
    type: String,
    default: "暂无数据",
  },
});

const mergedHeaderCells = ref<Set<number>>(new Set());
const mergedBodyCells = ref<Set<string>>(new Set());

const isHeaderCellMerged = (colIndex: number): boolean => {
  return mergedHeaderCells.value.has(colIndex);
};

const isBodyCellMerged = (rowIndex: number, colIndex: number): boolean => {
  return mergedBodyCells.value.has(`${rowIndex}-${colIndex}`);
};

const getColspan = (
  row: Record<string, any>,
  rowIndex: number,
  column: TableColumn,
  colIndex: number
): number => {
  if (column.mergeCell) {
    const { colspan } = column.mergeCell(
      row,
      rowIndex,
      column,
      colIndex,
      props.data
    );

    if (colspan > 1) {
      for (let i = 1; i < colspan; i++) {
        mergedBodyCells.value.add(`${rowIndex}-${colIndex + i}`);
      }
    }
    return colspan;
  }

  if (column.mergeByField && column.prop) {
    const currentValue = getCellValue(row, column.prop);
    let colspan = 1;

    for (let i = colIndex + 1; i < props.columns.length; i++) {
      const nextColumn = props.columns[i];
      if (nextColumn.prop !== column.prop) break;

      const nextValue = getCellValue(row, nextColumn.prop);
      if (nextValue === currentValue) {
        colspan++;
        mergedBodyCells.value.add(`${rowIndex}-${i}`);
      } else {
        break;
      }
    }
    return colspan;
  }

  return 1;
};

const getRowspan = (
  row: Record<string, any>,
  rowIndex: number,
  column: TableColumn,
  colIndex: number
): number => {
  if (column.mergeCell) {
    const { rowspan } = column.mergeCell(
      row,
      rowIndex,
      column,
      colIndex,
      props.data
    );

    if (rowspan > 1) {
      for (let i = 1; i < rowspan; i++) {
        mergedBodyCells.value.add(`${rowIndex + i}-${colIndex}`);
      }
    }
    return rowspan;
  }

  if (column.mergeByField && column.prop) {
    const currentValue = getCellValue(row, column.prop);
    let rowspan = 1;

    for (let i = rowIndex + 1; i < props.data.length; i++) {
      const nextRow = props.data[i];
      const nextValue = getCellValue(nextRow, column.prop);

      if (nextValue === currentValue) {
        rowspan++;
        mergedBodyCells.value.add(`${i}-${colIndex}`);
      } else {
        break;
      }
    }
    return rowspan;
  }

  return 1;
};

const getCellValue = (row: Record<string, any>, prop: string): any => {
  return row[prop] ?? "";
};

onMounted(() => {
  props.columns.forEach((column, colIndex) => {
    if (column.headerColspan && column.headerColspan > 1) {
      for (let i = 1; i < column.headerColspan; i++) {
        mergedHeaderCells.value.add(colIndex + i);
      }
    }
  });
});
</script>

<style scoped lang="scss">
.simple-table-wrapper {
  width: 100%;
  box-sizing: border-box;
  padding: 16px;
}

.table-title {
  margin: 0 0 16px 0;
  font-size: 18px;
  font-weight: 600;
  color: #333;
}

.table-outer {
  width: 100%;
  box-sizing: border-box;
  border-radius: 4px;
  overflow: hidden;
}

.table-header-fixed {
  width: 100%;
  box-sizing: border-box;

  border-bottom: 1px solid transparent;
}

.table-body-scroll {
  width: 100%;
  box-sizing: border-box;
  overflow: auto;

  &::-webkit-scrollbar {
    width: 2px; /* 纵向滚动条宽度 */
  }
}

.simple-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

.table-header-cell {
  padding: 8px;
  font-weight: 500;
  color: #606266;
  background-color: #f5f7fa;
  border: 1px solid #e4e7ed;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.table-row {
  transition: background-color 0.2s ease;
}

.table-cell {
  padding: 8px;
  color: #303133;
  border: 1px solid #e4e7ed;
  border-top: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.striped .table-row:nth-child(even) {
  background-color: #fafafa;
}

.hover-row:hover {
  background-color: #f5f7fa;
}

.empty-cell {
  padding: 40px 0;
  text-align: center;
  border: 1px solid #e4e7ed;
  border-top: none;
}

.empty-state {
  display: inline-block;
  padding: 20px;
}

.empty-text {
  color: #909399;
  font-size: 14px;
}
</style>
